package Server;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.rmi.MarshalledObject;
import java.rmi.RemoteException;
import java.rmi.activation.Activatable;
import java.rmi.activation.ActivationID;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;

import General.EventInterface;
import General.Product;
import General.QuoteImplementation;
import General.ShipInterface;



public class ShipImplementation implements ShipInterface, Serializable {	
	private static final long serialVersionUID = 2L;
	
	private static final String REQUEST = "Request";
	private static final String READY = "Ready";
	private static final String SENT = "Sent";
	private static final String ARRIVED = "Arrived";
	
	private String fName;
	private int fRateGround;
	private int fRateAir;
	private int fRateAirPriority;
	private HashMap<Product, String> fProductStatus = new HashMap<Product, String>();
	private Vector<Product> fComplete = new Vector<Product>();
	private HashMap<Product, EventInterface> fProductEvent = new HashMap<Product, EventInterface>();
	/*
	public ShipImplementation(ActivationID id, MarshalledObject<?> data) throws RemoteException {
		super(id, 18828);
	}
*/
	public ShipImplementation(String name, int rateGround, int rateAir, int rateAirPriority) throws IOException {
		super();
		fName = name;
        this.changeRates(rateGround, rateAir, rateAirPriority);
        UnicastRemoteObject.exportObject(this, 1097, new SslRMIClientSocketFactory(), new SslRMIServerSocketFactory());
	}
	  
	public void changeRates(int rateGround, int rateAir, int rateAirPriority) throws RemoteException, IOException {
        fRateGround = rateGround;
        fRateAir = rateAir;
        fRateAirPriority = rateAirPriority;
	}

	public QuoteImplementation quote(Product product) throws RemoteException {
		for (int i=0; i < 1000; i++) {
			//
		}
		if (product.getTypeTransport().contentEquals(Product.GROUND)) {
			return new QuoteImplementation(product.getWeight() * fRateGround);
		} else if (product.getTypeTransport().contentEquals(Product.AIR)) {
			return new QuoteImplementation(product.getWeight() * fRateAir);
		} else if (product.getTypeTransport().contentEquals(Product.AIR_PRIORITY)) {
			return new QuoteImplementation(product.getWeight() * fRateAirPriority);
		}
		return new QuoteImplementation(product.getWeight() * 15);
	}

	public boolean request(Product product, EventInterface event) throws RemoteException, IOException {	
		event.addNotification("Product (" + product.getName() + ") status changed to: " + REQUEST);
		
		fProductEvent.put(product, event);
		fProductStatus.put(product, REQUEST);
		return true;
	}
	
	public String status(Product product) throws RemoteException {
		if (fComplete.contains(product)) {
			return ARRIVED;
		}
		return fProductStatus.get(product);
	}
	
	public Vector<Product> pendingShipments() throws RemoteException {
		Vector<Product> vector = new Vector<Product>();
		Iterator<Map.Entry<Product, String>> it = fProductStatus.entrySet().iterator();
	    while (it.hasNext()) {
	        Map.Entry<Product, String> pairs = (Map.Entry<Product, String>)it.next();
	        if (!pairs.getValue().toString().contentEquals(ARRIVED)) {
		        vector.add((Product) pairs.getKey());
	        }
	        //it.remove();
	    }
	    return vector;
	}
	
	public Product getPending(int index) throws RemoteException {
		int count = 0;
		for (Product product : fProductStatus.keySet()) {
		    if (count == index) {
		    	return product;
		    }
		    count++;
		}
		return null;
	}
	
	public Product getComplete(int index) throws RemoteException {
		if (fComplete.size() <= index) {
			return null;
		}
		return fComplete.elementAt(index);
	}
	
	public String serialize() throws RemoteException, IOException {
		System.out.println("[SERVER] Serialize to file.");
		
		fProductEvent.clear();
		
		FileOutputStream fileOut = new FileOutputStream(fName + ".ser");
		ObjectOutputStream out = new ObjectOutputStream(fileOut);
		out.writeObject(this);
		out.close();
		fileOut.close();
		return fName + ".ser";
	}
	
	public ShipInterface deserialize(String file) throws RemoteException, IOException, ClassNotFoundException {
		System.out.println("[SERVER] Loading server from file.");
		FileInputStream fileIn = new FileInputStream(file);
		ObjectInputStream in = new ObjectInputStream(fileIn);
		ShipImplementation ship = (ShipImplementation) in.readObject();
        UnicastRemoteObject.exportObject(ship, 1091, new SslRMIClientSocketFactory(), new SslRMIServerSocketFactory());
		in.close();
		fileIn.close();
		return ship;
	}
	
	public void updateStatus() throws RemoteException, IOException {
		Product[] keySet = (Product[]) fProductStatus.keySet().toArray(new Product[fProductStatus.size()]);
		for (Product product : keySet) {
		    String status = fProductStatus.get(product);
		    
		    if (!status.contentEquals(ARRIVED)) {
				if (status.contentEquals(REQUEST)) {
					status = READY;
				} else if (status.contentEquals(READY)) {
					status = SENT;
				} else if (status.contentEquals(SENT)) {
					status = ARRIVED;
				} 
				fProductStatus.put(product, status);
				fProductEvent.get(product).addNotification("Product (" + product.getName() + ") status changed to: " + status);
		    }
		    if (status.contentEquals(ARRIVED)) {
		    	if (!fComplete.contains(product)) {
			    	fComplete.add(product);
		    	}
				fProductStatus.remove(product);
		    }
		}
	}
}